package org.dromara.common.encrypt.filter

import cn.hutool.core.util.ObjectUtil
import jakarta.servlet.*
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import org.dromara.common.core.constant.HttpStatus
import org.dromara.common.core.exception.ServiceException
import org.dromara.common.core.utils.SpringUtils.getBean
import org.dromara.common.core.utils.StringUtils
import org.dromara.common.encrypt.annotation.ApiEncrypt
import org.dromara.common.encrypt.properties.ApiDecryptProperties
import org.springframework.http.HttpMethod
import org.springframework.web.method.HandlerMethod
import org.springframework.web.servlet.HandlerExceptionResolver
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
import java.io.IOException

/**
 * Crypto 过滤器
 *
 * @author wdhcr
 * @updater LikeYouDo
 * @date 2025/1/14 13:24
 */
open class CryptoFilter(
    private val properties: ApiDecryptProperties
) : Filter {
    @Throws(IOException::class, ServletException::class)
    override fun doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain) {
        val servletRequest = request as HttpServletRequest
        val servletResponse = response as HttpServletResponse
        // 获取加密注解
        val apiEncrypt: ApiEncrypt? = this.getApiEncryptAnnotation(servletRequest)
        val responseFlag = apiEncrypt != null && apiEncrypt.response
        var requestWrapper: ServletRequest? = null
        var responseWrapper: ServletResponse? = null
        var responseBodyWrapper: EncryptResponseBodyWrapper? = null

        // 是否为 put 或者 post 请求
        if (HttpMethod.PUT.matches(servletRequest.method) || HttpMethod.POST.matches(servletRequest.method)) {
            // 是否存在加密标头
            val headerValue = servletRequest.getHeader(properties.headerFlag)
            if (StringUtils.isNotBlank(headerValue)) {
                // 请求解密
                requestWrapper = DecryptRequestBodyWrapper(servletRequest, properties.privateKey, properties.headerFlag)
            } else {
                // 是否有注解，有就报错，没有放行
                if (apiEncrypt != null) {
                    val exceptionResolver = getBean(
                        "handlerExceptionResolver",
                        HandlerExceptionResolver::class.java
                    )
                    exceptionResolver.resolveException(
                        servletRequest, servletResponse, null,
                        ServiceException("没有访问权限，请联系管理员授权", HttpStatus.FORBIDDEN)
                    )
                    return
                }
            }
        }

        // 判断是否响应加密
        if (responseFlag) {
            responseBodyWrapper = EncryptResponseBodyWrapper(servletResponse)
            responseWrapper = responseBodyWrapper
        }

        chain.doFilter(
            ObjectUtil.defaultIfNull(requestWrapper, request),
            ObjectUtil.defaultIfNull(responseWrapper, response)
        )

        if (responseFlag) {
            servletResponse.reset()
            // 对原始内容加密
            val encryptContent = responseBodyWrapper!!.getEncryptContent(
                servletResponse, properties.publicKey, properties.headerFlag
            )
            // 对加密后的内容写出
            servletResponse.writer.write(encryptContent)
        }
    }

    /**
     * 获取 ApiEncrypt 注解
     */
    private fun getApiEncryptAnnotation(servletRequest: HttpServletRequest): ApiEncrypt? {
        val handlerMapping = getBean(
            "requestMappingHandlerMapping",
            RequestMappingHandlerMapping::class.java
        )
        // 获取注解
        try {
            val mappingHandler = handlerMapping.getHandler(servletRequest)
            if (mappingHandler != null) {
                val handler = mappingHandler.handler
                if (handler is HandlerMethod) {
                    return handler.getMethodAnnotation(ApiEncrypt::class.java)
                }
            }
        } catch (e: Exception) {
            return null
        }
        return null
    }

    override fun destroy() {
    }

}
